package com.flying.common;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;

import com.flying.common.log.Logger;
import com.flying.common.util.BeanUtils;
import com.flying.common.util.Codes;
import com.flying.common.util.JSONUtils;
import com.flying.common.util.ServiceHelper;
import com.flying.common.util.StringUtils;
import com.flying.framework.annotation.Service;
import com.flying.framework.annotation.service.RequestType;
import com.flying.framework.data.Data;
import com.flying.framework.data.DataUtils;
import com.flying.framework.exception.AppException;
import com.flying.framework.model.QueryResult;
import com.flying.framework.module.LocalModule;
import com.flying.framework.request.AbstractRequestService;

@RequestType("csv")
@Service("CsvRequestService")
public class CsvRequestService extends AbstractRequestService{
    private static final String SEPARATOR = ",";
	private final static Logger log = Logger.getLogger(CsvRequestService.class);
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public void service(LocalModule module, String[] uris, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		final String s = uris[uris.length - 2];
		final String service = this.serviceConfig.getConfig(s) == null? s : this.serviceConfig.getConfig(s); 
		final String method = uris[uris.length - 1];
		final String view = req.getParameter("v");
		Data request = DataUtils.convert(module, req);
		try {
			//
			final InputStream is = req.getInputStream();
			MessageBody messageBody = null;
			if(is != null && "POST".equalsIgnoreCase(req.getMethod())) {
				messageBody = new MessageBody(is);
			}
			if(messageBody != null) request.putAll(messageBody.jsonData);
			request.put(Codes.PAGE_NUM, 0);
			request.put(Codes.PAGE_SIZE, 100000);
			//
			Data formData = ServiceHelper.invoke("pas", "FormService:findByModuleAndCode", new Data("module", module.getId(),"code", view));
			Long formId = formData.getLong("formId");
			List<Data> fields = ServiceHelper.invoke("pas", "FieldService:findByFormId", new Data("form_id", formId)).get("rows");
			List<String> titles = fields.stream().filter(f -> !"hidden".equalsIgnoreCase(f.getString("displayType")) && f.getInteger("isColumn", 0) == 1 ).map(f -> f.getString("title")).collect(Collectors.toList());
			List<String> fieldNames = fields.stream().filter(f -> !"hidden".equalsIgnoreCase(f.getString("displayType")) && f.getInteger("isColumn", 0) == 1 ).map(f -> f.getString("name")).collect(Collectors.toList());

			QueryResult result = (QueryResult)module.invoke(service+":"+method, request);
			List<Data> rows = result.getRows();
			//
			resp.setContentType("application/ms-txt.numberformat:@");
			resp.setCharacterEncoding("UTF-8");
			resp.setHeader("Pragma", "public");
			resp.setHeader("Cache-Control", "max-age=30");
			resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(formData.getString("name")+".csv", "UTF-8"));
	        //
			resp.getOutputStream().write(new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF});
			try(var output = new BufferedWriter(new OutputStreamWriter(resp.getOutputStream(), "UTF-8"), 1024)) {
				titles.forEach(title -> {
					try {
						output.write(title+SEPARATOR);
					} catch (IOException e) {
						log.error("Csv Error", e);
					}
				});
				output.newLine();
				rows.stream().forEach(row -> {
					fieldNames.stream().forEach(field -> {
						String fieldValue = row.getString(field);
						if(fieldValue == null) {
							try {
								fieldValue = BeanUtils.getProperty(row, field).toString();
							} catch (Exception e) {
								fieldValue ="-";
							}
						}
						if(!StringUtils.isEmpty(fieldValue)) {
							fieldValue = fieldValue.replaceAll(",", "/");
						}
						try {
							output.write(fieldValue+SEPARATOR);
						} catch (IOException e) {
							log.error("Csv Error", e);
						}
					});
					try {
						output.newLine();
					} catch (IOException e) {
						log.error("Csv Error", e);
					}
				});
			}
			resp.flushBuffer();
		} catch (Exception e) {
			log.error("DoRequestSerice error", e);
			resp.sendError(500);
		}
	}
	
	class MessageBody {
		String messageBody = "";
		Data jsonData = new Data();
		
		public MessageBody(InputStream is) {
			try {
				messageBody = IOUtils.toString(is, "UTF-8");
				if(messageBody == null || "".equals(messageBody.trim())) return ;
				//
				final int start = messageBody.indexOf("{");
				messageBody = messageBody.substring(start);
				//
				Map<String, Object> map = JSONUtils.toMap(messageBody);
				this.jsonData = new Data(map);
			} catch (Exception e) {
				throw new AppException(e,"参数错误，JSON解析错误！", String.valueOf(Codes.INVALID_PARAM));
			}
		}
	}
}
